package com.baturu.vin.benz.service.impl.util;

import com.baturu.vin.benz.dto.BenzEPCPartsInfoDTO;
import com.baturu.vin.benz.dto.BenzPartCatInfoDTO;
import com.baturu.vin.benz.dto.BenzSuperPartsInfoDTO;
import com.baturu.vin.benz.dto.BenzVinInfoDTO;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 奔驰vin码和配件编码解析器
 * String类型数据处理
 *
 * @author 蒋鑫磊, Majiamminmg
 * @date 2016/11/15.
 */
@Slf4j
public class BenzStringUtil {
    private static final Pattern CHASS_IDENT_PATTERN = Pattern.compile("[A-Z]\\d{7}");
    private static final Pattern LEFT_PARENTHESIS_PATTERN = Pattern.compile("\\u002B");
    private static final Pattern SLASH_PATTERN = Pattern.compile("//");
    private static final Pattern TZ_PATTERN = Pattern.compile("TZ.*");


    public static Set<String> getSaCodeSet(Collection<String> saCodeList) {
        if (CollectionUtils.isEmpty(saCodeList)) {
            return Collections.emptySet();
        }
        Set<String> saCodeSet = Sets.newHashSet();
        String saCodes = "";
        for (String sacodeData : saCodeList) {
            if (StringUtils.isNotBlank(sacodeData)) {
                sacodeData = sacodeData.trim();
                if (saCodes.length() > 0) {
                    int len = saCodes.length();
                    if (len % 4 != 0) {
                        saCodes = pad(saCodes, len + (4 - len % 4));
                    }
                }
                saCodes += sacodeData;
            }
        }
        addCode(saCodeSet, saCodes);
        return saCodeSet;
    }

    public static void addCode(Set<String> saCodeSet, String codeString) {
        if (codeString != null) {
            int temp = codeString.length() % 4;
            if (temp != 0) {
                codeString += "    ".substring(0, 4 - temp);
            }
            for (int i = 0; i < codeString.length(); i += 4) {
                String currentcode = codeString.substring(i, i + 4);
                if (StringUtils.isNotBlank(currentcode)) {
                    try {
                        saCodeSet.add(currentcode.trim());
                    } catch (Exception e) {
                        log.warn("处理SA码:{}出错", codeString);
                    }
                }
            }
        }
    }

    private static String pad(String curString, int newLen) {
        if (StringUtils.isBlank(curString)) {
            return Strings.repeat(" ", newLen);
        }
        return Strings.padEnd(curString, newLen, ' ');
    }


    public static String buildAppInf(String location) {

        String market;
        if (location.startsWith("P") || location.startsWith("C")) {
            market = location.substring(1);
            switch (market) {
                case "NA":
                    return "F";
                case "JA":
                    return "S";
                case "LA":
                    return "W";
                case "SA":
                    return "K";
                case "SM":
                    return "M";
                default:
                    return "1";
            }
        }
        switch (location) {
            case "NA":
                return "F";
            case "JA":
                return "S";
            case "LA":
                return "W";
            case "SA":
                return "K";
            case "SM":
                return "M";
            case "EX":
                return "1";
            default:
                return "1";
        }
    }


    private static String modifyCodeBAccessoryPart(String codeB) {
        boolean matches;
        String updatedCodeB = codeB;
        if (codeB != null && !"".equals(codeB)) {
            StringTokenizer st = new StringTokenizer(codeB, "+-/()[]");
            while (st.hasMoreTokens()) {
                String token = st.nextToken();
                Matcher matcher = TZ_PATTERN.matcher(token);
                matches = matcher.matches();
                if (matches) {
                    int index = updatedCodeB.indexOf(token);
                    char c = ' ';
                    if (index >= 1 && (updatedCodeB.charAt(index - 1) == '+' || updatedCodeB.charAt(index - 1) == '-' || (updatedCodeB.charAt(index - 1) == '/' && updatedCodeB.length() - 5 <= index - 1))) {
                        --index;
                        c = updatedCodeB.charAt(index);
                        token = "@" + token;
                        updatedCodeB = updatedCodeB.replace(c, '@');
                    }
                    updatedCodeB = updatedCodeB.replaceFirst(token, "");
                    updatedCodeB = updatedCodeB.replace('@', c);
                }

            }

            int occurance = updatedCodeB.lastIndexOf("/");
            int count = 0;
            while (count <= occurance) {
                Matcher matcher2 = SLASH_PATTERN.matcher(updatedCodeB);
                if (matcher2.find()) {
                    if (matcher2.end() == updatedCodeB.length()) {
                        updatedCodeB = updatedCodeB.substring(0, matcher2.start());
                    } else if (matcher2.end() < updatedCodeB.length()) {
                        updatedCodeB = updatedCodeB.replace("//", "/");
                    }
                    ++count;
                } else {
                    if (updatedCodeB.endsWith("/")) {
                        updatedCodeB = updatedCodeB.substring(0, updatedCodeB.length() - 1);
                        break;
                    }
                    break;
                }
            }
        }
        return updatedCodeB;
    }

    public static boolean isAccessoryRecord(String codeB, String source) {
        boolean matches;
        if (StringUtils.isNotBlank(codeB) && "D".equals(source)) {
            StringTokenizer st = new StringTokenizer(codeB, "+-/()[]");
            while (st.hasMoreTokens()) {
                String token = st.nextToken();
                Matcher matcher = TZ_PATTERN.matcher(token);
                matches = matcher.matches();
                if (matches) {
                    return true;
                }
            }
        }
        return false;
    }

    public static int scoreCodeBString(String saCode, Set<String> saCodeSet) {
        int score = 0;
        StringTokenizer st = new StringTokenizer(saCode, "/");
        while (st.hasMoreTokens()) {
            String curCodeB = st.nextToken();
            if (!checkSingleSAMatch(curCodeB, saCodeSet)) {
                continue;
            }
            StringTokenizer scoreTokenizer = new StringTokenizer(curCodeB, "+-");
            int curCodeBScore = scoreTokenizer.countTokens();
            curCodeBScore -= countStringOccurances(curCodeB);
            if (curCodeBScore <= score) {
                continue;
            }
            score = curCodeBScore;
        }
        return score;
    }

    private static int countStringOccurances(String str) {
        int count = 0;
        int index;
        while ((index = str.indexOf("-")) != -1) {
            ++count;
            str = str.substring(index + 1);
        }
        return count;
    }

    public static boolean checkSingleSAMatch(String saCode, Set<String> saCodeSet) {
        if (StringUtils.isBlank(saCode)) {
            return false;
        }
        // + 拆分
        String[] split = LEFT_PARENTHESIS_PATTERN.split(saCode);
        if (split.length == 1 && !saCode.contains("-")) {
            if (!split[0].contains("-")) {
                return saCodeSet.contains(split[0]);
            }
        }
        Set<String> splitSACodeSet = Sets.newHashSet(split).stream().filter(StringUtils::isNotBlank).collect(Collectors.toSet());
        Set<String> hasSaCodeSet = splitSACodeSet.stream().filter(e -> !e.contains("-")).collect(Collectors.toSet());
        Set<String> hasNoSaCodeSet = splitSACodeSet.stream().filter(e -> e.contains("-")).collect(Collectors.toSet());
        Set<String> hasSaCodeSet2 = hasNoSaCodeSet.stream().filter(e -> !e.startsWith("-")).map(e -> e.split("-")[0]).collect(Collectors.toSet());
        hasSaCodeSet.addAll(hasSaCodeSet2);
        hasNoSaCodeSet = hasNoSaCodeSet.stream().map(e -> {
            if (!e.startsWith("-")) {
                return e.substring(e.split("-")[0].length());
            }
            return e;
        }).flatMap(e -> {
            if (!e.contains("-")) {
                return Sets.newHashSet(e).stream();
            }
            return Sets.newHashSet(e.split("-")).stream();
        }).flatMap(e -> {
            if (e.startsWith("-")) {
                return Sets.newHashSet(e.split("-")).stream();
            }
            String[] split1 = e.split("-");
            Set<String> saCodes = Sets.newHashSet();
            Collections.addAll(saCodes, split1);
            return saCodes.stream();
        })
                .filter(StringUtils::isNotBlank)
                .collect(Collectors.toSet());

        boolean hasSaCodeSetMatch = true;
        boolean hasNoCodeSetMatch = true;

        if (CollectionUtils.isNotEmpty(hasSaCodeSet)) {
            hasSaCodeSetMatch = checkHasIntersection(saCodeSet, hasSaCodeSet, true);
        }
        if (CollectionUtils.isNotEmpty(hasNoSaCodeSet)) {
            hasNoCodeSetMatch = !checkHasIntersection(saCodeSet, hasNoSaCodeSet, false);
        }
        return hasSaCodeSetMatch && hasNoCodeSetMatch;
    }

    private static boolean checkHasIntersection(Set<String> saCodeSet, Set<String> checkSaSet, boolean needFullInterSe) {
        Set<String> saCodeSet2 = checkSaSet.stream().filter(StringUtils::isNotBlank).collect(Collectors.toSet());
        Set<String> allSaCode = saCodeSet2.stream().map(e -> {
            if (e.length() == 3 && e.endsWith("O")) {
                return e.substring(0, 2) + "0";
            }
            return e;
        }).collect(Collectors.toSet());
        Sets.SetView<String> commonSaCode = Sets.intersection(saCodeSet, allSaCode);
        if (needFullInterSe) {
            return commonSaCode.size() == allSaCode.size();
        }
        return !commonSaCode.isEmpty();
    }

    public static String getCatClassNo(List<BenzPartCatInfoDTO> allCatInfos) {
        int count1 = 0;
        int count2 = 0;
        int count3 = 0;
        int countF = 0;
        for (BenzPartCatInfoDTO allCatInfo : allCatInfos) {
            if ("1".equals(allCatInfo.getClassNo())) {
                count1++;
            } else if ("2".equals(allCatInfo.getClassNo())) {
                count2++;
            } else if ("3".equals(allCatInfo.getClassNo())) {
                count3++;
            } else {
                countF++;
            }
        }
        List<Integer> primes = Arrays.asList(count1, count2, count3, countF);
        IntSummaryStatistics stats = primes.stream().mapToInt(x -> x).summaryStatistics();
        int max = stats.getMax();
        if (count1 == max) {
            return "1";
        }
        if (count2 == max) {
            return "2";
        }
        if (count3 == max) {
            return "3";
        }
        if (countF == max) {
            return "F";
        }
        return "1";
    }

    public static boolean filterBySteering(BenzEPCPartsInfoDTO p, BenzVinInfoDTO benzVinInfoDTO) {
        BenzPartCatInfoDTO transCatInfo = benzVinInfoDTO.getCatInfoMap().get(benzVinInfoDTO.getShortTransmission());
        if (transCatInfo == null) {
            return true;
        }
        String vehSteering = " ";
        String chassIdent = benzVinInfoDTO.getChassIdent();
        String substring = chassIdent.substring(0, 1);
        if ("1".equals(substring) || "5".equals(substring)) {
            vehSteering = "L";
        }
        if ("2".equals(substring) || "6".equals(substring)) {
            vehSteering = "R";
        }

        if (" ".equals(vehSteering)) {
            return true;
        }
        String classNo = transCatInfo.getClassNo();
        String transType = transCatInfo.getAggType();
        boolean auto = false;
        boolean manual = false;
        if (StringUtils.isNotBlank(transType) && "GA".equals(transType)) {
            auto = true;
        }
        if (StringUtils.isNotBlank(transType) && "GM".equals(transType)) {
            manual = true;
        }
        String partSteer = p.getSteering();
        if (StringUtils.isBlank(partSteer)) {
            partSteer = "";
        }
        if (partSteer.length() < 4) {
            int padding = 4 - partSteer.length();
            for (int i = 0; i < padding; i++) {
                partSteer += " ";
            }
        }
        if ("1".equals(classNo) || "2".equals(classNo) || "3".equals(classNo)) {
            switch (partSteer) {
                case "    ":
                    return true;
                case "X   ":
                    return !("R".equals(vehSteering) || auto);
                case " X  ":
                    return !("R".equals(vehSteering) || manual);
                case "  X ":
                    return !("L".equals(vehSteering) || auto);
                case "   X":
                    return !("L".equals(vehSteering) || manual);
                case "X X ":
                    return !auto;
                case " X X":
                    return !manual;
                case "XX  ":
                    return !"R".equals(vehSteering);
                case "  XX":
                    return !"L".equals(vehSteering);
                case "X  X":
                    return !("L".equals(vehSteering) && auto || "R".equals(vehSteering) && manual);
                case " XX ":
                    return !("L".equals(vehSteering) && manual || "R".equals(vehSteering) && auto);
                case "XXX ":
                    return !("R".equals(vehSteering) && auto);
                case " XXX":
                    return !("L".equals(vehSteering) && manual);
                case "XX X":
                    return !("R".equals(vehSteering) && manual);
                case "X XX":
                    return !("L".equals(vehSteering) && auto);
                default:
                    return true;
            }
        }
        String version = p.getSteering();
        if (partSteer.charAt(0) == 'X' || partSteer.charAt(1) == 'X') {
            return !("R".equals(version) || "R".equals(vehSteering));
        } else if ((partSteer.charAt(2) == 'X' || partSteer.charAt(3) == 'X')) {
            return !("L".equals(version) || "L".equals(vehSteering));
        }
        return true;
    }

    public static String buildFitChass(String chassIdent) {
        //chassIdent只有8位,可能是A1234567也可能是AA123456,只取一个字母后面一串数字的
        Matcher matcher = CHASS_IDENT_PATTERN.matcher(chassIdent);
        if (matcher.find()) {
            return chassIdent;
        }
        return chassIdent.substring(chassIdent.length() - 7);
    }

    public static boolean filterNotFitSa(Set<String> saCodeSet, String saCode) {
        return StringUtils.isBlank(saCode) || checkSingleSAMatch(saCode, saCodeSet);
    }

    public static List<String> buildFootNoteList(List<BenzEPCPartsInfoDTO> benzEPCPartsInfoDTOs) {
        benzEPCPartsInfoDTOs.forEach(p -> {
            String footnotes = p.getFootnotes();
            if (StringUtils.isBlank(footnotes)) {
                footnotes = "";
            }
            if (footnotes.length() >= 6 && footnotes.length() < 9) {
                p.setFootNoteList(Lists.newArrayList(footnotes.substring(0, 3), footnotes.substring(3, 6)));
            } else if (footnotes.length() >= 9 && footnotes.length() < 12) {
                p.setFootNoteList(Lists.newArrayList(footnotes.substring(0, 3), footnotes.substring(3, 6),
                        footnotes.substring(6, 9)));
            } else if (footnotes.length() >= 12) {
                p.setFootNoteList(Lists.newArrayList(footnotes.substring(0, 3), footnotes.substring(3, 6),
                        footnotes.substring(6, 9), footnotes.substring(9, 12)));
            } else {
                p.setFootNoteList(Lists.newArrayList(footnotes));
            }
        });
        return benzEPCPartsInfoDTOs.stream().flatMap(p -> p.getFootNoteList().stream()).collect(Collectors.toList());
    }

    public static boolean filterBySaCode(BenzEPCPartsInfoDTO p, Set<String> saCodeSet, Map<String, BenzPartCatInfoDTO> catInfoMapByCatNum) {
        String codeB = p.getSaCode();
        if (p.isAccessoryPart()) {
            codeB = BenzStringUtil.modifyCodeBAccessoryPart(codeB);
        }
        return BenzValidateCodeBUtil.validateCodeB(saCodeSet, codeB, catInfoMapByCatNum.get(p.getCatNum()).getSource());
    }

    public static boolean filterBySaCode(BenzSuperPartsInfoDTO p, Set<String> saCodeSet, BenzPartCatInfoDTO chass) {
        String codeB = p.getSaCode();
        if (BenzStringUtil.isAccessoryRecord(codeB, chass.getSource())) {
            codeB = BenzStringUtil.modifyCodeBAccessoryPart(codeB);
        }
        return BenzValidateCodeBUtil.validateCodeB(saCodeSet, codeB, chass.getSource());
    }

    private static boolean checkNumeric(String target) {
        if (StringUtils.isBlank(target) || target.trim().length() == 0) {
            return false;
        }
        if (target.charAt(0) == '-') {
            target = target.substring(1);
        }
        if (target.length() == 0) {
            return false;
        }
        for (int i = 0; i < target.length(); i++) {
            if (!Character.isDigit(target.charAt(i))) {
                return false;
            }
        }
        return true;
    }

    public static boolean isAllDigits(String str, int startidx, int len) {
        boolean valid = true;
        for (int i = startidx; i < len; i++) {
            if (!Character.isDigit(str.charAt(i))) {
                valid = false;
                break;
            }
        }
        return valid;
    }

    public static String removeBlank(String target) {
        StringBuilder buf = new StringBuilder();
        for (int i = 0; i < target.length(); i++) {
            char c = target.charAt(i);
            if (c != ' ') {
                buf.append(c);
            }
        }
        return buf.toString();
    }

    public static String reformatDate(String inDate) {
        String day = "";
        String month = "";
        String year = "";
        if (BenzStringUtil.checkNumeric(inDate.trim())) {
            if (inDate.length() >= 8) {
                day = inDate.substring(6, 8);
                month = inDate.substring(4, 6);
                year = inDate.substring(2, 4);
            } else if (inDate.length() >= 6) {
                day = inDate.substring(4, 6);
                month = inDate.substring(2, 4);
                year = inDate.substring(0, 2);
            }
            try {
                Integer iYear = new Integer(year);
                if (iYear > 59) {
                    year = "19" + year;
                } else {
                    year = "20" + year;
                }
                return year + month + day;
            } catch (NumberFormatException e) {
                log.error("日期转换出错");
            }
        }
        return null;
    }

    public static String getTempDate(String releaseDate, String deliveryDate) {
        String tempDate = "";
        if (releaseDate != null && !"".equals(releaseDate.trim()) && releaseDate.trim().length() >= 6) {
            tempDate = reformatDate(releaseDate);
        } else if (deliveryDate != null && !"".equals(deliveryDate.trim()) && deliveryDate.trim().length() >= 6) {
            tempDate = reformatDate(deliveryDate);
        }
        if (tempDate == null) {
            tempDate = "";
        }
        return tempDate;
    }

    public static String getCountry(String whc) {
        if (whc.startsWith("W")) {
            return "欧洲";
        } else if (whc.startsWith("L")) {
            return "中国";
        } else if ("9BM".equals(whc)) {
            return "巴西";
        } else if ("4JG".equals(whc)) {
            return "美国";
        } else if ("3MB".equals(whc)) {
            return "墨西哥";
        } else {
            return "";
        }
    }

    public static String shortString(String str) {
        if (StringUtils.isNotBlank(str)) {
            if (str.length() >= 6) {
                return str.substring(0, 6);
            }
        }
        return "";
    }

}
